package org.simplextensions.scanner.impl;

import javassist.bytecode.ClassFile;
import org.simplextensions.scanner.ClassFileIterator;
import org.simplextensions.scanner.ClassScannerBundle;
import org.simplextensions.scanner.ClasspathScannerException;
import org.simplextensions.scanner.IClassScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * 
 * @author Tomasz Krzyzak, <a
 *         href="mailto:tomasz.krzyzak@gmail.com">tomasz.krzyzak@gmail.com</a>
 * @since 31-08-2010 21:21:23
 */
public class DirCSB extends ClassScannerBundle {

	private static final Logger log = LoggerFactory.getLogger(DirCSB.class);

	private final File dir;

	public class DirClassFileIterator extends ClassFileIterator {

		private Queue<File> files2Process = new ConcurrentLinkedQueue<File>();

		public DirClassFileIterator(DirCSB csb) {
			super(csb);
			if (getClassNamesFilter().size() == 0) {
				try {
					log.debug("Scanning dir: " + getDir().getCanonicalPath() + " for classes");
				} catch (IOException e) {
					// do nothing
				}
				this.files2Process.add(getDir());
			} else {
				log.debug("Scanning classes specified in descriptor");
				for (String s : getClassNamesFilter()) {
					files2Process.add(new File(getDir(), s.replace(".", File.separator) + ".class"));
				}
			}

		}

		protected ClassFile searchNext() {
			ClassFile result = null;
			while (result == null && !files2Process.isEmpty()) {
				File polled = files2Process.poll();
				if (!polled.exists())
					continue;

				if (polled.isFile()) {
					if (polled.getName().endsWith(".class")) {
						InputStream inputStream = null;
						try {
							String className = polled.getCanonicalPath().substring(getDir().getCanonicalPath().length() + 1);
							className = className.substring(0, className.length() - (className.length() > 6 ? 6 : 0)).replace(
									File.separatorChar, '.');
							inputStream = new FileInputStream(polled);
							result = new ClassFile(new DataInputStream(inputStream));
						} catch (Exception e) {
							throw new ClasspathScannerException("Cant open/read class file: " + polled.getAbsolutePath(), e);
						} finally {
							if (inputStream != null) {
								try {
									inputStream.close();
								} catch (IOException e1) {
									// no need to do anything
								}
							}
						}
					}
				} else {
					files2Process.addAll(Arrays.asList(polled.listFiles()));
				}
			}

			return result;
		}

	}

	public DirCSB(String name, File dir, Map<String, Map<Integer, List<IClassScanner>>> scanListeners, Set<String> classNames) {
		super(name, scanListeners, classNames);
		this.dir = dir;
	}

	public Iterator<ClassFile> classFileIterator() {
		return new DirClassFileIterator(this);
	}

	public File getDir() {
		return dir;
	}

}
